package step

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	schcomm "github.com/wanchain/go-mpc/storeman/schnorrcomm"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type RandomPolynomialGenUni struct {
	BaseGenerator
	randCoefficient []big.Int                   //coefficient
	message         map[discover.NodeID]big.Int //Polynomial result
	polyValue       []big.Int
	polyValueSigR   []*big.Int
	polyValueSigS   []*big.Int
	result          *big.Int
	smpcer          mpcprotocol.SchnorrMPCer
	ContextId       uint64
	sigIndex        int
}

func createSkPolyGenUni(degree int, peerNum int, sigIndex int, smpcer mpcprotocol.SchnorrMPCer) *RandomPolynomialGenUni {
	return &RandomPolynomialGenUni{
		BaseGenerator{},
		make([]big.Int, degree+1),
		make(map[discover.NodeID]big.Int),
		make([]big.Int, peerNum),
		make([]*big.Int, peerNum),
		make([]*big.Int, peerNum),
		nil,
		smpcer, 0, sigIndex}
}

func (poly *RandomPolynomialGenUni) initialize(totalNum int,
	result mpcprotocol.MpcResultInterface) error {

	poly.SetContextId(result.GetMpcContextId())

	log.Info("RandomPolynomialGenUni::initialize ", "len of recieved message", len(poly.message), "ctx", poly.GetContextId())

	// get randCoefficient
	_, grpIdString, _ := osmconf.GetGrpId(result)

	selfIndex, err := osmconf.GetOsmConf().GetSelfInx(grpIdString)
	if err != nil {
		log.SyslogErr("RandomPolynomialGenUni::initialize", "initialize", err.Error(), "ctx", poly.GetContextId())
	}
	key := mpcprotocol.RPolyCoff + strconv.Itoa(int(selfIndex))
	poly.randCoefficient, _ = result.GetValue(key)

	// check threshold and len(poly.randCoefficient)
	threshold, _ := osmconf.GetOsmConf().GetThresholdNum(grpIdString)
	if threshold < 1 || (len(poly.randCoefficient)/int(threshold) != 0 && len(poly.randCoefficient)/int(threshold) < poly.sigIndex) {
		err := errors.New(fmt.Sprintf("RandomPolynomialGenUni::initialize RandomPolynomialGenUni initialize GetThresholdNum threshold = %v", threshold))
		log.SyslogErr(err.Error(), "ctx", poly.GetContextId())
		return err
	}
	degree := int(threshold) - 1

	// get x = hash(pk)
	for i := 0; i < len(poly.polyValue); i++ {
		nodeId, _ := osmconf.GetOsmConf().GetNodeIdByIndex(grpIdString, uint16(i))
		//nodeId := &(*peers)[i].PeerID
		xValue, err := osmconf.GetOsmConf().GetXValueByNodeId(grpIdString, nodeId, poly.smpcer)
		if err != nil {
			log.SyslogErr("RandomPolynomialGenUni::initialize", "initialize.GetXValueByNodeId", err.Error(), "ctx", poly.GetContextId())
		}
		rcvIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, nodeId)

		poly.polyValue[i] = poly.smpcer.EvaluatePoly(poly.randCoefficient[poly.sigIndex*(degree+1):(poly.sigIndex+1)*(degree+1)],
			xValue,
			degree)

		h := sha256.Sum256(poly.polyValue[i].Bytes())
		prv, err := osmconf.GetOsmConf().GetSelfPrvKey()
		if err != nil {
			log.SyslogErr("RandomPolynomialGenUni::initialize", "GetSelfPrvKey error", err.Error(), "ctx", poly.GetContextId())
			return err
		}

		poly.polyValueSigR[i], poly.polyValueSigS[i], _ = schcomm.SignInternalData(prv, h[:])
		log.SyslogDebug("RandomPolynomialGenUni::initialize poly ",
			"group id", grpIdString,
			"senderPk", hexutil.Encode(crypto.FromECDSAPub(&prv.PublicKey)),
			"senderIndex", selfIndex,
			"rcvIndex", rcvIndex,
			"poly peerId", nodeId.SlimString(),
			"poly x seed", hexutil.Encode(xValue.Bytes()),
			"sigR", hexutil.Encode(poly.polyValueSigR[i].Bytes()),
			"sigS", hexutil.Encode(poly.polyValueSigS[i].Bytes()),
			"h", hexutil.Encode(h[:]), "ctx", poly.GetContextId())
	}

	return nil
}

func (poly *RandomPolynomialGenUni) calculateResult() error {
	poly.result = big.NewInt(0)
	log.Info("RandomPolynomialGenUni::calculateResult ", "len of recieved message", len(poly.message), "ctx", poly.GetContextId())

	return nil
}

func (poly *RandomPolynomialGenUni) SetSchnorrMpcer(smcer mpcprotocol.SchnorrMPCer) error {
	poly.smpcer = smcer
	return nil
}

func (poly *RandomPolynomialGenUni) SetContextId(contextId uint64) {
	poly.ContextId = contextId
}

func (poly *RandomPolynomialGenUni) GetContextId() uint64 {
	return poly.ContextId
}

func (poly *RandomPolynomialGenUni) SetMpcContext(mpc MpcContextFunc) {
	poly.mpc = mpc
}

func (poly *RandomPolynomialGenUni) GetMpcContext() MpcContextFunc {
	return poly.mpc
}
